package cn.iocoder.yudao.framework.pay.core.client.impl.hftx;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig;
import cn.iocoder.yudao.framework.pay.core.client.impl.hftx.vo.*;
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.DefaultAlipayClient;
import com.huifu.bspay.sdk.opps.core.utils.RsaUtils;
import com.huifu.bspay.sdk.opps.core.utils.SequenceTools;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;

/**
 * 模拟支付的 PayClient 实现类
 * <p>
 * 模拟支付返回结果都是成功，方便大家日常流畅
 *
 * @author jason
 */
@Slf4j
public class HftxPayClient extends AbstractPayClient<NonePayClientConfig> {

    @Getter // 仅用于单测场景
    protected DefaultAlipayClient client;

    private static final String MOCK_RESP_SUCCESS_DATA = "MOCK_SUCCESS";

    public HftxPayClient(Long channelId, NonePayClientConfig config) {
        super(channelId, PayChannelEnum.HFTX.getCode(), config);
    }

    @Override
    protected void doInit() {
    }

    @Override
    protected PayOrderRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws Exception {
        // 构建汇付天下支付请求对象
        HftxPayRequestParam requestParam = buildRequestParam(reqDTO);
        log.info("[汇付天下支付请求数据：{}]", JSONObject.toJSONString(requestParam));
        // 执行支付请求
        Map<String, Object> response = HftxWxPayService.doPayRequest(requestParam);
        switch (Objects.requireNonNull(TradeTypeEnum.getByCode(requestParam.getTradeType()))) {
            case T_MINIAPP:
                String payInfoStr = String.valueOf(response.get("pay_info"));
                JSONObject payInfo = JSON.parseObject(payInfoStr);
                // 转换结果
                return PayOrderRespDTO.waitingOf(PayOrderDisplayModeEnum.APP.getMode(), toJsonString(payInfo),
                        reqDTO.getOutTradeNo(), response);
            case A_NATIVE:
                String codeUrl = String.valueOf(response.get("qr_code"));
                return PayOrderRespDTO.waitingOf(PayOrderDisplayModeEnum.QR_CODE.getMode(), codeUrl,
                        reqDTO.getOutTradeNo(), response);
        }
        return null;
    }

    private HftxPayRequestParam buildRequestParam(PayOrderUnifiedReqDTO reqDTO) {
        DecimalFormat df = new DecimalFormat("#0.00");
        return HftxPayRequestParam.builder()
                .reqSeqId(reqDTO.getOutTradeNo())
                .goodsDesc(reqDTO.getSubject())
                .tradeType(PayChannelEnum.HFTXALI.getCode().equals(reqDTO.getChannelExtras().get("payType")) ? TradeTypeEnum.A_NATIVE.getCode() : TradeTypeEnum.T_MINIAPP.getCode())
                .transAmt(df.format(reqDTO.getPrice() / 100.0))
                .subOpenId(reqDTO.getChannelExtras().get("openid"))
                .notifyUrl(reqDTO.getNotifyUrl())
                .goodsDetail(buidldHftxGoodsDetail(reqDTO))
                .build();
    }

    private HftxPayRequestParam.HftxGoodsDetail buidldHftxGoodsDetail(PayOrderUnifiedReqDTO reqDTO) {
        DecimalFormat df = new DecimalFormat("#0.00");
        // 商品列表明细
        List<JSONObject> wxRucan = new ArrayList<>();
        JSONObject wxRucanJson = new JSONObject();
        // TODO 商品id
        wxRucanJson.put("goodsId", SequenceTools.getReqSeqId32());
        wxRucanJson.put("goodsName", reqDTO.getSubject());
        wxRucanJson.put("price", df.format(reqDTO.getPrice() / 100.0));
        wxRucanJson.put("quantity", "1");
        // TODO 微信侧商品id
        wxRucanJson.put("wxpayGoodsId", SequenceTools.getReqSeqId32());
        wxRucan.add(wxRucanJson);
        return HftxPayRequestParam.HftxGoodsDetail.builder()
                .costPrice(df.format(reqDTO.getPrice() / 100.0))
                .receiptId(reqDTO.getOutTradeNo())
                .wxRucan(JSON.toJSONString(wxRucan))
                .build();
    }

    @Override
    protected PayOrderRespDTO doGetOrder(String outTradeNo) throws Exception {
        // 汇付支付订单交易查询
        HftxPayRequestParam requestParam = HftxPayRequestParam.builder()
                .reqSeqId(outTradeNo)
                .build();
        Map<String, Object> response = HftxWxPayService.doPayQueryRequest(requestParam);
        PayOrderStatusRespEnum status = PayOrderStatusRespEnum.WAITING;
        if (TransStatEnum.S.getCode().equals(response.get("trans_stat"))) {
            status = PayOrderStatusRespEnum.SUCCESS;
        }
        DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        String payDate = (String) response.get("org_req_date");
        LocalDate date = LocalDate.parse(payDate, inputFormatter);
        return PayOrderRespDTO.of(status.getStatus(), outTradeNo, (String) response.get("bagent_id"), LocalDateTimeUtil.of(date),
                outTradeNo, response);
    }

    @Override
    protected PayRefundRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Exception {
        HftxRefundRequestParam hftxRefundRequestParam = buildRefundRequestParam(reqDTO);
        log.info("汇付支付退款请求参数：{}", JSONObject.toJSONString(hftxRefundRequestParam));
        // 发起退款请求 TODO 退款失败-可能跟汇付商户直连模式有关
        Map<String, Object> response = HftxWxPayService.doRefundRequest(hftxRefundRequestParam);
        return PayRefundRespDTO.failureOf("MOCK-R-" + reqDTO.getOutRefundNo(), "退款失败",
                reqDTO.getOutRefundNo(), MOCK_RESP_SUCCESS_DATA);
    }

    private HftxRefundRequestParam buildRefundRequestParam(PayRefundUnifiedReqDTO reqDTO) {
        DecimalFormat df = new DecimalFormat("#0.00");
        JSONObject reqData = JSONObject.parseObject(reqDTO.getNotifyData(), JSONObject.class);
        JSONObject rowData = reqData.getJSONObject("rawData");
        return HftxRefundRequestParam.builder()
                .reqSeqId(reqDTO.getOutRefundNo()) // 退款单号
                .ordAmt(df.format(reqDTO.getRefundPrice() / 100.0)) // 退款金额
                .notifyUrl(reqDTO.getNotifyUrl()) // 异步通知地址
                .huifuId(rowData.getString("huifu_id")) // 汇付Id
                .orgReqDate(rowData.getString("req_date")) // 发起时间
                .reqDate(DateUtil.format(LocalDateTime.now(), "yyyyMMdd")) // 发起时间
                .orgHfSeqId(rowData.getString("hf_seq_id")) // 原交易全局流水号
                .orgReqSeqId(reqDTO.getOutTradeNo()) // 原交易订单号
                .build();
    }

    @Override
    protected PayRefundRespDTO doGetRefund(String outTradeNo, String outRefundNo) {
        return PayRefundRespDTO.successOf("MOCK-R-" + outRefundNo, LocalDateTime.now(),
                outRefundNo, MOCK_RESP_SUCCESS_DATA);
    }

    @Override
    protected PayTransferRespDTO doParseTransferNotify(Map<String, String> params, String body) {
        throw new UnsupportedOperationException("未实现");
    }

    @Override
    protected PayRefundRespDTO doParseRefundNotify(Map<String, String> params, String body) {
        throw new UnsupportedOperationException("模拟支付无退款回调");
    }

    @Override
    protected PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body) {
        // 1. 解析回调
        HftxPayOrderNotifyResult response = parseOrderNotifyResult(params);
        // 2. 构建结果
        return PayOrderRespDTO.of(response.getResponseCode(), PayChannelEnum.HFTX.getCode(), response.getChannelUserId(), response.getPaySuccessTime(),
                response.getOutTradeNo(), response.getResponseData());
    }

    private HftxPayOrderNotifyResult parseOrderNotifyResult(Map<String, String> params) {
        HftxPayOrderNotifyResult hftxPayOrderNotifyResult = new HftxPayOrderNotifyResult();
        String data = params.get("resp_data");
        try {
            // 验签请参sign
            String sign = params.get("sign");
            // 使用汇付公钥验签
            if (!RsaUtils.verify(data, DemoTestConstants.DEMO_RSA_PUBLIC_KEY, sign)) {
                // 验签失败处理
                return hftxPayOrderNotifyResult;
            }
            JSONObject dataObj = JSON.parseObject(data);
            String subRespCode = dataObj.getString("sub_resp_code");
            String reqSeqId = dataObj.getString("req_seq_id");
            if ("00000000".equals(subRespCode)) {
                // 业务处理成功
                hftxPayOrderNotifyResult.setResponseCode(PayOrderStatusRespEnum.SUCCESS.getStatus());
                // 微信小程序支付获取支付用户的openid
                if (TradeTypeEnum.T_MINIAPP.getCode().equals(dataObj.getString("trade_type"))) {
                    hftxPayOrderNotifyResult.setChannelUserId(dataObj.getJSONObject("wx_response").getString("sub_openid"));
                }
                // 交易类型
                hftxPayOrderNotifyResult.setTradType(dataObj.getString("trade_type"));
                // 外部订单号
                hftxPayOrderNotifyResult.setOutTradeNo(reqSeqId);
                // 交易订单号
                hftxPayOrderNotifyResult.setTransactionId(dataObj.getString("out_trans_id"));
                // 微信侧商户号
                hftxPayOrderNotifyResult.setMchId(dataObj.getString("atu_sub_mer_id"));
                // 微信侧商户号订单号
                hftxPayOrderNotifyResult.setPartyOrderId(dataObj.getString("party_order_id"));
                // 订单支付成功时间
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
                LocalDateTime paySuccessTime = LocalDateTime.parse(dataObj.getString("end_time"), formatter);
                hftxPayOrderNotifyResult.setPaySuccessTime(paySuccessTime);
            } else {
                // 业务处理失败
                hftxPayOrderNotifyResult.setResponseCode(PayOrderStatusRespEnum.WAITING.getStatus());
            }
            // 回调数据
            hftxPayOrderNotifyResult.setResponseData(data);
            return hftxPayOrderNotifyResult;
        } catch (Exception e) {
            log.info("异步回调开始，参数，request={}", data, e);
        }
        return hftxPayOrderNotifyResult;
    }

    @Override
    protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) {
        throw new UnsupportedOperationException("待实现");
    }

    @Override
    protected PayTransferRespDTO doGetTransfer(String outTradeNo, PayTransferTypeEnum type) {
        throw new UnsupportedOperationException("待实现");
    }

}
